package com.mzplayer.videoview.base;

import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.media.AudioManager;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;

import com.mzplayer.utils.Util;
import com.mzplayer.widget.FloatContainer;

/**
 * @Description: 标准父类
 * 窗口切换和触摸相关逻辑
 * 继承本类处理UI相关逻辑自由度较高
 * 参考StandardVideoView
 * @Author: 麦子
 * @QQ: 3332174009
 * @CreateDate: 2021/6/27
 */
public abstract class StandardParent extends BaseParent {
    public static final int SCREEN_NORMAL = 0;
    public static final int SCREEN_FULL = 1;
    public static final int SCREEN_FLOAT = 2;
    private int screenState = SCREEN_NORMAL;
    protected static final int defaultDelay = 5000;
    private ViewGroup parent;
    private ViewGroup.LayoutParams layoutParams;
    private int index;
    private View heir;
    private boolean fullScreenVertical;
    private FloatContainer floatContainer;
    private final DisplayMetrics metrics = Util.getScreenMetrics(context);
    private int floatSize = Math.min(metrics.widthPixels, metrics.heightPixels) /3;
    private int floatPadding = (int) (metrics.density * 10 + 0.5f);
    private final GestureDetector detector = new GestureDetector(context, new OnGestureListener());
    private final Runnable hRunnable = new HideRunnable();

    public StandardParent(Context context) {
        this(context, null);
    }

    public StandardParent(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public StandardParent(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    protected abstract int getBackgroundColor();

    public void setFloatSize(int floatSize) {
        this.floatSize = floatSize;
    }

    public void setFloatPadding(int floatPadding) {
        this.floatPadding = floatPadding;
    }

    public void release() {
        reset();
        toggleNormal();
    }

    protected abstract boolean isLocked();

    public abstract boolean isShowing();

    public abstract void show();

    public abstract void hide();

    public void show(int delay) {
        removeCallbacks(hRunnable);
        if (!isShowing()) {
            show();
        }
        if (delay != 0) {
            postDelayed(hRunnable, delay);
        }
    }

    protected void remove(View view) {
        if (view == null) {
            return;
        }
        ViewGroup parent = (ViewGroup) view.getParent();
        if (parent == null) {
            return;
        }
        parent.removeView(view);
    }

    public void setHeir(Object heir) {
        this.heir = Util.getView(heir);
        remove(this.heir);
    }

    private void saveParent() {
        if (this.parent != null) {
            return;
        }
        ViewGroup parent = (ViewGroup) getParent();
        if (parent == null) {
            return;
        }
        this.parent = parent;
        layoutParams = getLayoutParams();
        index = parent.indexOfChild(this);
        parent.removeView(this);
        if(heir!=null) {
            remove(heir);
            parent.addView(heir, index, layoutParams);
        }
    }

    private void resumeParent() {
        if (parent != null) {
            remove(heir);
            parent.addView(this, index, layoutParams);
            parent = null;
            layoutParams = null;
            index = 0;
            Util.requestFocus(this);
        }
    }

    public void setFullScreenVertical(boolean fullScreenVertical) {
        this.fullScreenVertical = fullScreenVertical;
        if (screenState == SCREEN_FULL && !isLocked()) {
            checkFullScreenOrientation();
        }
    }

    public boolean getFullScreenVertical() {
        return fullScreenVertical;
    }

    private void checkFullScreenOrientation() {
        if (fullScreenVertical) {
            Util.setScreenOrientation(context, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
        } else {
            Util.setScreenOrientation(context, ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
        }
    }

    public int getScreenState() {
        return screenState;
    }

    public void toggleFull() {
        if (isLocked()) {
            return;
        }
        if (screenState == SCREEN_FULL) {
            return;
        } else if (screenState == SCREEN_NORMAL) {
            saveParent();
        } else if (screenState == SCREEN_FLOAT) {
            if (floatContainer != null) {
                floatContainer.dismiss();
                floatContainer = null;
            }
        }
        Util.hideStatusBar(context);
        Util.hideVirtualKey(context);
        checkFullScreenOrientation();
        ViewGroup decorView = (ViewGroup) Util.scanForActivity(context).getWindow().getDecorView();
        decorView.addView(this, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        Util.requestFocus(this);
        screenState = SCREEN_FULL;
        onToggleFull();
    }

    protected abstract void onToggleFull();

    private void clearFull() {
        Util.showStatusBar(context);
        Util.showVirtualKey(context);
        Util.setScreenOrientation(context, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
        remove(this);
        clearFocus();
    }

    public void toggleNormal() {
        if (isLocked()) {
            return;
        }
        if (screenState == SCREEN_FULL) {
            clearFull();
        } else if (screenState == SCREEN_NORMAL) {
            return;
        } else if (screenState == SCREEN_FLOAT) {
            if (floatContainer != null) {
                floatContainer.dismiss();
                floatContainer = null;
            }
        }
        resumeParent();
        screenState = SCREEN_NORMAL;
        onToggleNormal();
    }

    protected abstract void onToggleNormal();

    private int[] countFloatSize(int videoWidth, int videoHeight) {
        if (videoWidth == 0 || videoHeight == 0) {
            videoWidth = 16;
            videoHeight = 9;
        }
        return videoWidth > videoHeight ? new int[]{floatSize * videoWidth / videoHeight, floatSize} : new int[]{floatSize, floatSize * videoHeight / videoWidth};
    }

    public void toggleFloat() {
        if (isLocked()) {
            return;
        }
        if (!FloatContainer.canFloat(context)) {
            return;
        }
        if (screenState == SCREEN_FLOAT) {
            return;
        } else if (screenState == SCREEN_NORMAL) {
            saveParent();
        } else if (screenState == SCREEN_FULL) {
            clearFull();
        }
        floatContainer = new FloatContainer(this);
        int[] size = trView != null ? countFloatSize(trView.getVideoWidth(), trView.getVideoHeight()) : new int[]{floatSize * 16 / 9, floatSize};
        int x = metrics.widthPixels - size[0] - floatPadding;
        int y = metrics.heightPixels - size[1] - floatPadding;
        floatContainer.setSize(size[0], size[1]).setOffset(x, y).create();
        screenState = SCREEN_FLOAT;
        onToggleFloat();
    }

    protected abstract void onToggleFloat();

    @Override
    protected void onVideoSizeChanged(int width, int height, int sar_num, int sar_den) {
        if (floatContainer != null) {
            int[] size = countFloatSize(width, height);
            floatContainer.flushSize(size[0], size[1]);
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && screenState == SCREEN_FULL) {
            toggleNormal();
            return true;
        }
        return false;
    }

    protected static final int TYPE_NULL = -1;
    protected static final int TYPE_BRIGHT = 0;
    protected static final int TYPE_SEEK = 1;
    protected static final int TYPE_VOLUME = 2;
    private static final int MOVE_THRESHOLD = 50;
    private int moveType = TYPE_NULL;
    private int lastX;
    private int lastY;
    private int maxPosition;
    private int startPosition;
    private int endPosition;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        detector.onTouchEvent(event);
        if (isLocked()) {
            return true;
        }
        if (screenState == SCREEN_FLOAT) {
            floatTouch(event);
            return true;
        }
        return onTouch(event);
    }

    private boolean onTouch(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = (int) event.getX();
                lastY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (lastY <= floatPadding) {
                    break;
                }
                float deltaX = event.getX() - lastX;
                float deltaY = lastY - event.getY();
                if (moveType == TYPE_SEEK) {
                    endPosition = (int) Math.min(Math.max(deltaX / getWidth() * maxPosition + startPosition, 0), maxPosition);
                    onTouchTrackingMove(moveType, endPosition, maxPosition);
                } else if (moveType == TYPE_VOLUME) {
                    endPosition = (int) Math.min(Math.max(deltaY / getHeight() * maxPosition + startPosition, 0), maxPosition);
                    audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, endPosition, 0);
                    onTouchTrackingMove(moveType, endPosition, maxPosition);
                } else if (moveType == TYPE_BRIGHT) {
                    endPosition = (int) Math.min(Math.max(deltaY / getHeight() * maxPosition + startPosition, 0), maxPosition);
                    WindowManager.LayoutParams attributes = Util.scanForActivity(context).getWindow().getAttributes();
                    attributes.screenBrightness = endPosition / 255f;
                    Util.scanForActivity(context).getWindow().setAttributes(attributes);
                    onTouchTrackingMove(moveType, endPosition, maxPosition);
                } else {
                    float absDeltaX = Math.abs(deltaX);
                    float absDeltaY = Math.abs(deltaY);
                    if (absDeltaX > absDeltaY) {
                        if (absDeltaX > MOVE_THRESHOLD) {
                            maxPosition = (int) getDuration();
                            if (maxPosition == 0) {
                                break;
                            }
                            startPosition = (int) getCurrentPosition();
                            moveType = TYPE_SEEK;
                            onTouchTrackingStart(moveType, startPosition, maxPosition);
                        }
                    } else if (absDeltaY > MOVE_THRESHOLD) {
                        if (lastX > getWidth() * 0.5f) {
                            maxPosition = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
                            startPosition = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
                            moveType = TYPE_VOLUME;
                        } else {
                            maxPosition = 255;
                            startPosition = Util.getBrightness(context);
                            moveType = TYPE_BRIGHT;
                        }
                        onTouchTrackingStart(moveType, startPosition, maxPosition);
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                if (lastY <= floatPadding) {
                    break;
                }
                if (moveType != TYPE_NULL) {
                    onTouchTrackingStop(moveType, endPosition, maxPosition);
                }
                moveType = TYPE_NULL;
                break;
        }
        return true;
    }

    protected abstract void onTouchTrackingStart(int type, int position, int max);

    protected abstract void onTouchTrackingMove(int type, int position, int max);

    protected abstract void onTouchTrackingStop(int type, int position, int max);

    private void floatTouch(MotionEvent event) {
        int newX = (int) event.getRawX();
        int newY = (int) event.getRawY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = newX;
                lastY = newY;
                break;
            case MotionEvent.ACTION_MOVE:
                floatContainer.flushOffset(newX - lastX + floatContainer.getOffsetX(), newY - lastY + floatContainer.getOffsetY());
                lastX = newX;
                lastY = newY;
                break;
            case MotionEvent.ACTION_UP:
                DisplayMetrics metrics = Util.getScreenMetrics(context);
                int maxLeft = metrics.widthPixels - floatContainer.getWidth();
                int maxTop = metrics.heightPixels - floatContainer.getHeight();
                newX = lastX = floatContainer.getOffsetX();
                newY = lastY = floatContainer.getOffsetY();
                if (newX < 0) {
                    newX = floatPadding;
                } else if (newX > maxLeft) {
                    newX = maxLeft - floatPadding;
                }
                if (newY < 0) {
                    newY = 0;
                } else if (lastY > maxTop) {
                    newY = maxTop - floatPadding;
                }
                if (newX == lastX && newY == lastY) {
                    break;
                }
                PropertyValuesHolder pvhX = PropertyValuesHolder.ofInt("x", lastX, newX);
                PropertyValuesHolder pvhY = PropertyValuesHolder.ofInt("y", lastY, newY);
                ValueAnimator mAnimator = ObjectAnimator.ofPropertyValuesHolder(pvhX, pvhY);
                mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        floatContainer.flushOffset((int) animation.getAnimatedValue("x"), (int) animation.getAnimatedValue("y"));  //刷新显示
                    }
                });
                mAnimator.setDuration(300).start();
                break;
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (isShowing() && Util.isClick(event)) {
            show(defaultDelay);
        }
        return super.onInterceptTouchEvent(event);
    }

    class HideRunnable implements Runnable {
        @Override
        public void run() {
            hide();
        }
    }

    class OnGestureListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            if (!isLocked()) {
                doPauseAndPlay();
            }
            return true;
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            if (isShowing()) {
                hide();
            } else {
                show(defaultDelay);
            }
            return true;
        }
    }
}
